﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Duckbill
{
    public class Player : Enemy
    {
        static float jumpMaxVel = 375f;
        static float jumpStartVel = 175f;
        static float jumpAcc = 4000f;
        static float jumpTime = 0.175f;

        //Vector2 vel = Vector2.Zero;
        int facing = 1; //1 for facing right, -1 for facing left
        float maxSpeed = 300.0f;
        float xAccel = 1250f;
        float shootCooldown = 0.0f;
        float shootCooldownTime = 0.5f;

        int jumpLevel = 1;
        public int gunLevel = 0;

        KeyboardState oldKeys = Keyboard.GetState();

        public float MaxJumpHeight
        {
            get 
            {
                float accT = (jumpMaxVel-jumpStartVel)/(jumpAcc-gravity);
                float deaccT = jumpMaxVel/gravity;
                return (jumpStartVel+jumpMaxVel)/2*accT + jumpMaxVel*(jumpTime-accT) + jumpMaxVel/2*deaccT; 
            }
        }
        public float MaxJumpDist
        {
            get
            {
                float t = 2*(jumpTime + jumpMaxVel/gravity); //TODO: better time calculation
                return t*maxSpeed;
            }
        }
        public int JumpLevel
        {
            get
            {
                return jumpLevel;
            }
            set
            {
                jumpLevel = value;
                jumpMaxVel = 325 + 50*jumpLevel;
                //jumpStartVel = 170 + 5*jumpLevel;
                jumpAcc = 4000f + 100*jumpLevel;
                jumpTime = 0.125f + 0.05f*jumpLevel;
            }
        }

        public Player() : base("player", 100,  new Vector2(192, 256))
        {
            type = ObjectType.player;
            JumpLevel = 5;
        }

        void MotionLogic(float time, KeyboardState keys)
        {
            //get player input
            float moving = 0; //direction of motion
            if (keys.IsKeyDown(Keys.Left))
            {
                moving -= 1;
                facing = -1;
            }
            if (keys.IsKeyDown(Keys.Right))
            {
                moving += 1;
                facing = 1;
            }

            //decelerate quicker than normal if changing direction
            if (Math.Sign(moving) != Math.Sign(vel.X))
                moving *= 2;

            //dampen player-controlled motion if in air
            if (!onGround)
                moving *= 0.35f;

            //dampen velocity if player not pressing keys
            if (moving == 0)
            {
                float dX = -Math.Sign(vel.X) * xAccel * time;
                if (!onGround)
                    dX *= 0.15f;
                if (Math.Sign(vel.X + dX) != Math.Sign(vel.X))
                    vel.X = 0;
                else
                    vel.X += dX;
            }
            else //update velocity
            {
                vel.X += moving * xAccel * time;
                if (Math.Abs(vel.X) > maxSpeed)
                    vel.X = Math.Sign(moving) * maxSpeed;
            }
        }
        void JumpingLogic(float time, KeyboardState keys)
        {
            if (keys.IsKeyDown(Keys.Z))
            {
                if (onGround || (jumping <= jumpTime && jumping != 0f))
                {
                    if (jumping == 0f)
                    {
                        vel.Y = -jumpStartVel;
                        vel.X *= 0.8f;
                        onGround = false;
                    }
                    vel.Y = Math.Max(vel.Y - jumpAcc * time, -jumpMaxVel);
                    jumping += time;
                }
            }
            if (!keys.IsKeyDown(Keys.Z) || jumping > jumpTime)
            {
                jumping = 0f;
            }
        }
        void ShootingLogic(float time, KeyboardState keys, Dictionary<ObjectType, List<GameObject>> others)
        {
            if (gunLevel > 0)
            {
                shootCooldown = Math.Max(0f, shootCooldown-time);
                if (shootCooldown == 0f && keys.IsKeyDown(Keys.X))
                {
                    Vector2 P = pos + new Vector2(facing*sprite.size.X/2, sprite.size.Y/2);
                    Bullet B = new Bullet("bullet", true, P, new Vector2(facing*700, 0));
                    B.room = room;
                    others[ObjectType.playerAttack].Add(B);
                    shootCooldown = shootCooldownTime;
                }
            }
        }
        protected override void CollisionLogic(float time, Dictionary<ObjectType, List<GameObject>> others)
        {
            base.CollisionLogic(time, others);
            foreach (Tile tile in others[ObjectType.tile])
            {
                if ((tile.tileType != TileType.solid) && !tile.toRemove && 
                    !(Rectangle.Intersect(getRect(), tile.getRect()).IsEmpty))
                {
                    switch (tile.tileType)
                    {
                        case TileType.jumpItem:
                            JumpLevel += 1;
                            break;
                        case TileType.gunItem:
                            gunLevel += 1;
                            break;
                    }
                    tile.toRemove = true;
                }
            }
            foreach (Enemy enemy in others[ObjectType.enemy])
            {
                if (!Rectangle.Intersect(getRect(), enemy.getRect()).IsEmpty)
                {
                    int dir = Math.Sign((pos.X+sprite.size.X/2)-
                        (enemy.pos.X+enemy.sprite.size.X/2));
                    vel = new Vector2(dir*300, -200);
                    jumping = 0f;
                }
            }
            foreach (RoomTransition trans in others[ObjectType.transition])
            {
                if (!Rectangle.Intersect(getRect(), trans.getRect()).IsEmpty)
                {
                    GameState curr = Game.states.Top() as GameState;
                    Game.states.Pop();
                    Game.states.Push(new TransitionState(curr, 
                        new GameState(trans.newRoom), trans.direction));
                    Vector2 d = Vector2.Zero;
                    //TODO: more precise transitions
                    if (trans.sprite.size.X > trans.sprite.size.Y)
                    {
                        d.X = trans.pos.X - pos.X;
                        //d.Y = -sprite.size.X;
                    }
                    else
                    {
                        //d.X = -sprite.size.Y;
                        d.Y = trans.pos.Y - pos.Y;
                    }
                    pos = trans.newPos;
                    pos -= d;
                    room = trans.newRoom;
                }
            }
        }

        public override void Update(float time, Dictionary<ObjectType, List<GameObject>> others)
        {
            KeyboardState keys = Keyboard.GetState();

            if (keys.IsKeyDown(Keys.Enter) && !oldKeys.IsKeyDown(Keys.Enter))
                Game.states.Push(new MapState());

            MotionLogic(time, keys);
            DoGravity(time);
            JumpingLogic(time, keys);
            ShootingLogic(time, keys, others);
            CollisionLogic(time, others);

            oldKeys = keys;
        }

        public override void Move(float time)
        {
            pos += vel*time;
            if (room != null)
                room.UpdateCamera(pos, time);
        }
    }
}
